package asteroids.model.programs.expressions.ComposedExpressions;


import java.util.Arrays;

import asteroids.model.programs.expressions.BasicExpression;
import asteroids.model.programs.expressions.ComposedExpression;
import asteroids.model.programs.expressions.Expression;
import asteroids.model.programs.statements.Statement;
import asteroids.model.programs.types.Type;
import be.kuleuven.cs.som.annotate.*;
/**
* A class of binary expressions.
*   A binary expression involves an operator applied to a left-hand operand 
*   and a right-hand operand.
*   
*/

public abstract class BinaryExpression extends ComposedExpression {
	
	/**
	 * @param leftExp
	 * 		  The left operand of this binary expression
	 * @param rightExp
	 * 		  The right operand of this binary expression
	 * @post ...
	 * 		 | new.getLeftOperand() = leftExp
	 * @post ...
	 * 		 | new.getRightOperand() = rightExp
	 */
	protected BinaryExpression(Expression leftExp, Expression rightExp, int line, int column){
		super(line, column);
		leftOperand = leftExp;
		rightOperand =rightExp;
	}
	
	/**
	 * 
	 * @return this.leftOperand
	 * 
	 */
	@Basic
	public Expression getLeftOperand(){
		return leftOperand;
	}
	
	//Left Operand of the Binary expression
	private Expression leftOperand;
	
	/**
	 * 
	 * @return this.rightOperand
	 */
	@Basic
	public Expression getRightOperand(){
		return rightOperand;
	}
	
	// Right Operand of the Binary Expression
	private Expression rightOperand;
	
	/**
	 * Returns textual presentation of this binary expression
	 * 
	 * @return Left and right operands are basic expressions
	 * 			|if( leftOperand instanceof BasicExpression && rightOperand instanceof BasicExpression)
	 * 			|	return (leftOperand.toString() + getOperatorSymbol() + rightOperand.toString())
	 * 
	 * @return Left operand is a composed expression and right a basic.
	 * 			|if( leftOperand instanceof ComposedExpression && rightOperand instanceof BasicExpression)
	 * 			|	return ( "(" + leftOperand.toString() + ")" + getOperatorSymbol() + rightOperand.toString())
	 * 
	 * @return Left operand is a basic expression and right a composed.
	 * 			|if( leftOperand instanceof BasicExpression && rightOperand instanceof ComposedExpression)
	 * 			|	return ( leftOperand.toString() + getOperatorSymbol() + "(" + rightOperand.toString() + ")")
	 * 
	 * @return Both operands are composed expressions
	 * 			|if( leftOperand instanceof ComposedExpression && rightOperand instanceof ComposedExpression)
	 * 			|	return ( "(" + leftOperand.toString() + ")" + getOperatorSymbol() + "(" + rightOperand.toString() + ")")
	 */
	@Override
	public String toString(){
		String result;
		
		//convert left operand to string form
		if (leftOperand instanceof BasicExpression){
			result = leftOperand.toString();
		}
		else if (leftOperand instanceof ComposedExpression){
			result = "(" + leftOperand.toString() + ")";
		}
		else throw new Error("Unknown expression type, this should not happen");
		
		//Add the operator symbol
		result.concat(getOperatorSymbol());
		
		//convert and add the right operand to the string.
		if (rightOperand instanceof BasicExpression){
			result.concat(rightOperand.toString());
		}
		else if (rightOperand instanceof ComposedExpression){
			result.concat("(" + rightOperand.toString() + ")");
		}
		else throw new Error("Unknown expression type, this should not happen");
		
		return result;
	}
	
	/**
	 * Appends a line to the error message string if needed
	 * Then typechecks its operands.
	 */
	@Override
	public String typeCheckExpression(String errorMessage){
		Type leftType = getLeftOperand().getType();
		Type rightType = getRightOperand().getType();
		String newErrorMessage = null;
		String correctOperandType = "";
		for(int i = 0; i < getOperandType().length -1; i++){
			correctOperandType += "a " + getOperandType()[i].toString() + " or ";
		}
		correctOperandType += "a " + getOperandType()[getOperandType().length - 1].toString() + " ";
		
		if(!Arrays.asList(getOperandType()).contains(leftType) && !Arrays.asList(getOperandType()).contains(Type.ANY)){
			newErrorMessage = ("Expression at line " + getLeftOperand().getLine()
										+ " and column " + getLeftOperand().getColumn()
										+ " is a " + leftType.toString() + " and should be a "
										+ correctOperandType + "\n");
		}
		
		else if(!Arrays.asList(getOperandType()).contains(rightType) && !Arrays.asList(getOperandType()).contains(Type.ANY)){
			newErrorMessage = ("Expression at line " + getRightOperand().getLine()
										+ " and column " + getRightOperand().getColumn()
										+ " is a " + rightType.toString() + " and should be a "
										+ correctOperandType + "\n");
		}
		
		String leftMessage = getLeftOperand().typeCheckExpression(errorMessage) ;
		String rightMessage = getRightOperand().typeCheckExpression(errorMessage);
	
		if(leftMessage != null && newErrorMessage == null) newErrorMessage = leftMessage;
		else if(leftMessage != null && newErrorMessage != null) newErrorMessage += leftMessage;
		
		if(rightMessage != null && newErrorMessage == null) newErrorMessage = rightMessage;
		else if(rightMessage != null && newErrorMessage != null) newErrorMessage += rightMessage;
			
		return newErrorMessage;	
		
	}
	
	/**
	 * 
	 */
	@Override
	public void setStatement(Statement newStatement){
		super.setStatement(newStatement);
		leftOperand.setStatement(newStatement);
		rightOperand.setStatement(newStatement);
	}
	
}
